﻿namespace Nomo.CoreModule
{
    /// <summary>
    /// 提供一组直接操作内存的方法。
    /// </summary>
    public static unsafe class Memory
    {
        /// <summary>
        /// 将指定长度的一些字节从内存中的一个地址复制到另一个地址。如果两段内存空间存在重叠部分，结果可能会不符合预期，在这种情况下，请改用 <see cref="Move(void*,void*,ulong)"/> .
        /// </summary>
        /// <param name="src">要复制的字节的地址。</param>
        /// <param name="des">目标地址。</param>
        /// <param name="len">要复制的字节数。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="src"/> 或 <paramref name="des"/> 为 <see langword="null"/> .</exception>
        public static void Copy(void* src, void* des, ulong len)
        {
            if (src == null)
            {
                throw new System.ArgumentNullException(nameof(src));
            }
            if (des == null)
            {
                throw new System.ArgumentNullException(nameof(des));
            }
            if (len == 0UL)
            {
                return;
            }
            CopyInternal((byte*) src, (byte*) des, len);
        }

        /// <summary>
        /// 将指定长度的一些字节从内存中的一个地址复制到另一个地址。如果两段内存空间存在重叠部分，结果可能会不符合预期，在这种情况下，请改用 <see cref="Move(void*,ulong,void*,ulong,ulong)"/> .
        /// </summary>
        /// <param name="src">要复制的字节的地址。</param>
        /// <param name="scrOffset">要复制的字节的地址的偏移量。</param>
        /// <param name="des">目标地址。</param>
        /// <param name="desOffset">目标地址的偏移量。</param>
        /// <param name="len">要复制的字节数。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="src"/> 或 <paramref name="des"/> 为 <see langword="null"/> .</exception>
        public static void Copy(void* src, ulong scrOffset, void* des, ulong desOffset, ulong len)
        {
            if (src == null)
            {
                throw new System.ArgumentNullException(nameof(src));
            }
            if (des == null)
            {
                throw new System.ArgumentNullException(nameof(des));
            }
            if (len == 0UL)
            {
                return;
            }
            CopyInternal((byte*) src + scrOffset, (byte*) des + desOffset, len);
        }

        private static void CopyInternal(byte* src, byte* des, ulong len)
        {
            while (len-- != 0UL)
            {
                *des++ = *src++;
            }
        }

        /// <summary>
        /// 将指定长度的一些字节从内存中的一个地址复制到另一个地址。
        /// </summary>
        /// <param name="src">要复制的字节的地址。</param>
        /// <param name="des">目标地址。</param>
        /// <param name="len">要复制的字节数。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="src"/> 或 <paramref name="des"/> 为 <see langword="null"/> .</exception>
        public static void Move(void* src, void* des, ulong len)
        {
            if (src == null)
            {
                throw new System.ArgumentNullException(nameof(src));
            }
            if (des == null)
            {
                throw new System.ArgumentNullException(nameof(des));
            }
            if (len == 0UL)
            {
                return;
            }
            MoveInternal((byte*) src, (byte*) des, len);
        }

        /// <summary>
        /// 将指定长度的一些字节从内存中的一个地址复制到另一个地址。
        /// </summary>
        /// <param name="src">要复制的字节的地址。</param>
        /// <param name="scrOffset">要复制的字节的地址的偏移量。</param>
        /// <param name="des">目标地址。</param>
        /// <param name="desOffset">目标地址的偏移量。</param>
        /// <param name="len">要复制的字节数。</param>
        /// <exception cref="System.ArgumentNullException"><paramref name="src"/> 或 <paramref name="des"/> 为 <see langword="null"/> .</exception>
        public static void Move(void* src, ulong scrOffset, void* des, ulong desOffset, ulong len)
        {
            if (src == null)
            {
                throw new System.ArgumentNullException(nameof(src));
            }
            if (des == null)
            {
                throw new System.ArgumentNullException(nameof(des));
            }
            if (len == 0UL)
            {
                return;
            }
            MoveInternal((byte*) src + scrOffset, (byte*) des + desOffset, len);
        }

        private static void MoveInternal(byte* src, byte* des, ulong len)
        {
            if ((ulong) src >= (ulong) des || (ulong) src + len <= (ulong) des)
            {
                while (len-- != 0UL)
                {
                    *des++ = *src++;
                }
            }
            else
            {
                src += len - 1;
                des += len - 1;
                while (len-- != 0UL)
                {
                    *des-- = *src--;
                }
            }
        }
    }
}
